using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using CinemaLib;
using DBConnecter;
using System.Threading;

namespace ValidationController
{
    /// <summary>
    /// Makes sure that all inputs are alright before moving on.
    /// </summary>
    public static class Controller
    {
        /// <summary>
        /// Creates a user if user doesn't already exist.
        /// </summary>
        /// <param name="user">User.</param>
        public static void CreateUser(User user)
        {
            if (user.PassCode.Length < 6)
            {
                throw new FaultException("Password is too short");
                //return "Password is too short!";
            }
            else if (DatabaseController.UserExist(user))
            {
                throw new FaultException("This Email is already in use!");
                //"This Email is already in use, if you've forgot your password please click on \"Forgot Password?\" to get a new password.";
            }
            else if (user.FirstName == null || user.LastName == null || user.PassCode == null || user.EMail == null || new System.Text.RegularExpressions.Regex(@"[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,5}").IsMatch(user.EMail) || user.PhoneNumber.ToString().Length != 8)
            {
                throw new FaultException("User does not have alle nesserary information needed");
            }
            else
            {
                DatabaseController.CreateUser(user);
            }
        }

        /// <summary>
        /// Verifies a user with login information.
        /// </summary>
        /// <param name="username">Username.</param>
        /// <param name="passcode">Password.</param>
        /// <returns></returns>
        public static User VerifyUser(string userName, string passcode)
        {
            if (userName != null)
            {
                throw new FaultException("You must enter your username");
            }
            else if (passcode != null)
            {
                throw new FaultException("You must enter your password");
            }
            else
            {
                return DatabaseController.VerifyUser(userName, passcode);
            }
        }

        /// <summary>
        /// Implements the reservation to the database.
        /// </summary>
        /// <param name="show">The show that is chosen.</param>
        /// <returns>Returns the confirmation of the reservation.</returns>
        public static Reservation BeginReservation(Show show)
        {
            if (show == null)
            {
                throw new FaultException("No show defined");
            }
            else
            {
                Reservation reservation = DatabaseController.BeginReservation(show);
                Data.Add(reservation, DateTime.Now);

                return reservation;
            }
        }

        /// <summary>
        /// Updates the reservation.
        /// </summary>
        /// <param name="reservation">The reservation containing information about the relations.</param>
        public static void UpdateReservation(Reservation reservation)
        {
            if (reservation == null)
            {
                throw new FaultException("Invalid resevation");
            }
            else if (reservation.BookedShow == null)
            {
                throw new FaultException("Show not defined");
            }
            else
            {
                if (Data.Keys.First(r => r.ID == reservation.ID) != null)
                {
                    Data.Remove(Data.Keys.First(r => r.ID == reservation.ID));
                    Data.Add(reservation, DateTime.Now);
                }
                else
                {
                    Data.Add(reservation, DateTime.Now);
                }
                DatabaseController.UpdateReservation(reservation);
            }
        }

        /// <summary>
        /// Places an order assosiated with a user, reservation, show and writes it to the database.
        /// </summary>
        /// <param name="reservation">The reservation containing information about the relations.</param>
        public static void Order(Reservation reservation)
        {
            if (reservation == null)
            {
                throw new FaultException("You need to fill out the form");
            }
            else if (reservation.BookedUser == null)
            {
                throw new FaultException("Error in user");
            }
            else if (reservation.BookedUser.FirstName == null)
            {
                throw new FaultException("You need to fill out your first name");
            }
            else if (reservation.BookedUser.LastName == null)
            {
                throw new FaultException("You need to fill out your last name");
            }
            else if (reservation.BookedUser.PhoneNumber.ToString().Length != 8)
            {
                throw new FaultException("Invalid phone number");
            }
            else
            {
                DatabaseController.UpdateReservation(reservation);
                Data.Remove(Data.Keys.First(r => r.ID == reservation.ID));
            }
        }

        /// <summary>
        /// Cancels a reservation by it's ID.
        /// </summary>
        /// <param name="reservationId">Requests the reservation ID.</param>
        public static void Cancel(string reservationId)
        {
            if (reservationId == null)
            {
                throw new FaultException("No Resevation ID given");
            }
            else
            {
                DatabaseController.Cancel(reservationId);
                Data.Remove(Data.Keys.First(r => r.ID.ToString() == reservationId));
            }
        }

        /// <summary>
        /// Get a certain hall requested by ID.
        /// </summary>
        /// <param name="hallId">The number or name of a hall.</param>
        /// <param name="showId">The ID of the show.</param>
        /// <returns>A hall with rows and seats.</returns>
        public static Hall GetHall(int hallId, int showId)
        {
            return DatabaseController.GetHall(hallId, showId);
        }

        /// <summary>
        /// Requests a search of shows.
        /// </summary>
        /// <param name="searchWord">Searches by name.</param>
        /// <param name="searchStartTime">Searches from a specific time.</param>
        /// <param name="searchEndTime">Searches till a specific time.</param>
        /// <returns>Returns a list of shows.</returns>
        public static List<Show> GetShow(string searchWord, DateTime searchStartTime, DateTime searchEndTime)
        {
            if (searchStartTime > searchEndTime || searchStartTime == null || searchEndTime == null)
            {
                throw new FaultException("The timespan is not valid!");
            }
            else if (searchWord == null)
            {
                throw new FaultException("No search word was given");
            }
            else
            {
                return DatabaseController.GetShow(searchWord, searchStartTime, searchEndTime);
            }
        }

        /// <summary>
        /// Requests a search of shows within a specific timespan.
        /// </summary>
        /// <param name="hallId"></param>
        /// <param name="searchStartTime">Searches from a specific time.</param>
        /// <param name="searchEndTime">Searches till a specific time.</param>
        /// <returns>Returns a list of times of shows within the requested timespan.</returns>
        public static List<Show> GetSchedule(string hallId, DateTime searchStartTime, DateTime searchEndTime)
        {
            if (searchStartTime > searchEndTime || searchStartTime == null || searchEndTime == null)
            {
                throw new FaultException("the timespan is not valid!");
            }
            else if (hallId == null)
            {
                throw new FaultException("No hallid was given");
            }
            else
            {
                return DatabaseController.GetSchedule(hallId, searchStartTime, searchEndTime);
            }
        }

        /// <summary>
        /// Requests a list of shows in a specific timespan.
        /// </summary>
        /// <param name="movie">Searches for a movie.</param>
        /// <param name="searchStartTime">Searches from a specific time.</param>
        /// <param name="searchEndTime">Searches till a specific time.</param>
        /// <returns>A list of all shows with a certain movie within a timespan.</returns>
        public static List<Show> GetShowWithMovie(Movie movie, DateTime searchStartTime, DateTime searchEndTime)
        {
            if (searchStartTime > searchEndTime || searchStartTime == null || searchEndTime == null)
            {
                throw new FaultException("the timespan is not valid!");
            }
            else if (movie == null )
            {
                throw new FaultException("Invalid Movie data");
            }
            else
            {
                return DatabaseController.GetShowWithMovie(movie, searchStartTime, searchEndTime);
            }
        }

        /// <summary>
        /// Contains the timestamp of the latest reservation.
        /// </summary>
        static Dictionary<Reservation, DateTime> Data = new Dictionary<Reservation, DateTime>();

        /// <summary>
        /// Contains the time (5minutes) until it'll timeout.
        /// </summary>
        const int timeouttime = 5;

        /// <summary>
        /// Runs a loop that removes timedout reservations.
        /// </summary>
        public static void TimeoutHandler()
        {
            while(true)
            {
                Thread.Sleep(10000);
                for (int i = 0; i < Data.Count; i++)
                {
                    if ((int)(DateTime.Now.Subtract(Data.ElementAt(i).Value).Minutes) > timeouttime)
                    {
                        DatabaseController.RemoveTimeout(Data.ElementAt(i).Key);
                        Data.Remove(Data.ElementAt(i).Key);
                        i--;
                    }
                }
            }
        }

        /// <summary>
        /// Aborts from making a reservation.
        /// </summary>
        public static void Abort()
        {
            foreach (Reservation r in Data.Keys)
            {
                DatabaseController.Cancel(r.ID.ToString());
            }
        }

        /// <summary>
        /// Requests a list of the newest movies.
        /// </summary>
        /// <param name="number">Number of the movie.</param>
        /// <returns>Returns the newest movies.</returns>
        public static List<Movie> GetNewestMovies(int number)
        {
            return DatabaseController.GetNewestMovies(number);
        }
    }
}